﻿using CC.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace CC.UI
{
    /// <summary>
    /// 一个用于绘制天线方向图的控件，提供一下常用功能：
    /// 1. 方向图绘制
    /// 2. 单/双图模式
    /// 3. 是否显示图例
    /// 4. 图例锚定方式
    /// 5. 高亮曲线
    /// 6. 图上取点
    /// 7. 曲线上取点
    /// 8. 绘制包络线
    /// 9. 输出Markers点
    /// 10. 锁定曲线
    /// 
    /// 注意：调用时要删除掉自动产生的ChartAreas, Series, Legends
    /// </summary>
    public class RpChart:Chart, ISupportInitialize
    {
        #region Members
        private Series mEnvelopSerieH;//H面包络
        private Series mEnvelopSerieV;//V面包络
        //锁定的曲线
        private List<Series> mLockedSeries = new List<Series>();
        private ChartArea mAreaH = null;
        private Legend mLegendH = null;
        //保持绘图区、图例名称与系统默认一致，否则会出现莫名其妙的问题
        private string mHPlotName = "ChartArea1";
        private string mHLegendName = "Legend1";
        private ChartArea mAreaV = null;
        private Legend mLegendV = null;
        private string mVPlotName = "ChartArea2";
        private string mVLegendName = "Legend2";
        private bool _TwoPlotsMode = true;
        private bool _DockLegend = true;
        private int _DefaultWidth = 1;
        private int _HighlightWidth = 3;
        private double _ReferenceLineLevel = -3;
        private bool _PolarPlot = false;
        private bool _ShowLegend = true;
        private bool _EvelopMode;

        private bool _initialed = false;
        #endregion

        #region Extra Attributes
        [DefaultValue(false)]
        public bool AutoDrawReferLine { get; set; }
        public bool PolarPlot
        {
            get { return _PolarPlot; }
            set
            {
               
                _PolarPlot = value;
                if (Series.Count > 0)
                {
                    foreach (var serie in Series)
                    {
                        serie.ChartType = _PolarPlot ? SeriesChartType.Polar : SeriesChartType.Line;
                    }
                }
            }
        }
        [DefaultValue(true)]
        public bool TwoPlotsMode
        {
            get { return _TwoPlotsMode; }
            set
            {
               
                _TwoPlotsMode = value;
                if (value) SetTwoPlotsMode();
                else SetOnePlotMode();
            }
        }
        public bool ShowLegend
        {
            get { return _ShowLegend; }
            set
            {
                _ShowLegend = value;
                foreach (var serie in Series.Where(s => !IsReferLine(s)  && !IsEnvelopLine(s)))
                {
                    serie.IsVisibleInLegend = value;
                }
            }
        }
        public bool DockLegend
        {
            get { return _DockLegend; }
            set
            {
                _DockLegend = value;
                if (_DockLegend)
                {
                    if (ChartAreas.Contains(mAreaH))
                        mLegendH.DockedToChartArea = mHPlotName;
                    if (ChartAreas.Contains(mAreaV))
                        mLegendV.DockedToChartArea = mVPlotName;
                    else if (ChartAreas.Contains(mAreaH))
                        mLegendV.DockedToChartArea = mHPlotName;
                }
                else
                {
                    mLegendH.DockedToChartArea = "NotSet";
                    mLegendV.DockedToChartArea = "NotSet";
                }
            }
        }
        /// <summary>
        /// 包络线模式还是频标模式
        /// </summary>
        public bool EnvelopMode
        {
            get { return _EvelopMode; }
            set
            {
                _EvelopMode = value;
                Cursor = _EvelopMode ? Cursors.Cross : Cursors.Default;
            }
        }
        [DefaultValue(1)]
        public int DefaultWidth
        {
            get { return _DefaultWidth; }
            set
            {
                _DefaultWidth = value;
                foreach (var serie in Series.Where(s => s != CurrHit.Series))
                {
                    serie.BorderWidth = value;
                }
            }
        }
        [DefaultValue(3)]
        public int HighlightWidth
        {
            get { return _HighlightWidth; }
            set
            {
                _HighlightWidth = value;
                if (CurrHit.Series != null)
                {
                    CurrHit.Series.BorderWidth = value;
                }
            }
        }
        [Browsable(false)]
        public List<DataPoint> HMarkers { get; private set; }
        [Browsable(false)]
        public List<DataPoint> VMarkers { get; private set; }
        [Browsable(false)]
        public List<DataPoint> HEnvelopPoints { get; private set; }
        [Browsable(false)]
        public List<DataPoint> VEnvelopPoints { get; private set; }
        [Browsable(false)]
        public DataPoint CurrDataPoint { get; private set; }
        /// <summary>
        /// 当前点击结果
        /// </summary>
        [Browsable(false)]
        public HitTestResult CurrHit { get; private set; }
        /// <summary>
        /// 参考线相对于Y最大值的相对电平值
        /// 把值修改成0，关闭参考线
        /// </summary>
        [Category("Data")]
        [DefaultValue(-3)]
        public double ReferenceLineLevel { get { return _ReferenceLineLevel; } set { _ReferenceLineLevel = value; } }

        public double PhiMax
        {
            get
            {
                if (mAreaH == null) return double.NaN;
                return mAreaH.AxisX.Maximum;
            }

        }
        public double PhiMin
        {
            get
            {
                if (mAreaH == null) return double.NaN;
                return mAreaH.AxisX.Minimum;
            }

        }
        public double PhiInterval
        {
            get
            {
                if (mAreaH == null) return double.NaN;
                return mAreaH.AxisX.MajorGrid.Interval;
            }

        }
        public double ThetaMax
        {
            get
            {
                if (mAreaV == null) return double.NaN;
                return mAreaV.AxisX.Maximum;
            }

        }
        public double ThetaMin
        {
            get
            {
                if (mAreaV == null) return double.NaN;
                return mAreaV.AxisX.Minimum;
            }

        }
        public double ThetaInterval
        {
            get
            {
                if (mAreaV == null) return double.NaN;
                return mAreaV.AxisX.MajorGrid.Interval;
            }

        }
        [Browsable(false)]
        /// <summary>
        /// H面最大电平值
        /// </summary>
        public double MaxLevelHor
        {
            get
            {
                List<double> list = new List<double>();
                foreach (var s in this.Series.Where(s => s.ChartArea == mHPlotName && s != mEnvelopSerieH))
                {
                    list.Add(s.Points.Max(p => p.YValues[0]));
                }
                if (list.Count <= 0) return double.NaN;
                return list.Max();
            }
        }
        [Browsable(false)]
        /// <summary>
        /// V面最大电平值
        /// </summary>
        public double MaxLevelVer
        {
            get
            {
                if (mAreaV == null)
                {
                    return double.NaN;
                }
                else
                {
                    List<double> list = new List<double>();
                    foreach (var s in this.Series.Where(s => s.ChartArea == mVPlotName && s != mEnvelopSerieV))
                    {
                        list.Add(s.Points.Max(p => p.YValues[0]));
                    }
                    if (list.Count <= 0) return double.NaN;
                    return list.Max();
                }
            }
        } 
        #endregion
        public RpChart()
        {
            
            HMarkers = new List<DataPoint>();
            VMarkers = new List<DataPoint>();
            HEnvelopPoints = new List<DataPoint>();
            VEnvelopPoints = new List<DataPoint>();
            
            MouseMove += chart_MouseMove;
            MouseClick += chart_MouseClick;
            MouseDoubleClick += chart_MouseDoubleClick;
           

        }
        public new void EndInit()
        {
            _initialed = true;
            InitialChart();
        }
        private void InitialChart()
        {
            if (!_initialed) return;
            //确保仅有两个绘图区
            if (ChartAreas.Count < 1)
            {
                mAreaH = CreateChartArea(mHPlotName, -180, 180, 30);
                mAreaV = CreateChartArea(mVPlotName, -180, 180, 30);
            }
            else if (ChartAreas.Count < 2)
            {
                mAreaH = ChartAreas[0];
                mAreaH.Name = mHPlotName;
                SetAxial(mAreaH.AxisX, -180, 180, 30);
                mAreaV = CreateChartArea(mVPlotName, -180, 180, 30);
            }
            else
            {
                mAreaH = ChartAreas[0];
                mAreaH.Name = mHPlotName;
                SetAxial(mAreaH.AxisX, -180, 180, 30);
                mAreaV = ChartAreas[1];
                mAreaV.Name = mVPlotName;
                SetAxial(mAreaV.AxisX, -180, 180, 30);
                //删除多余的分区
                for (int i = ChartAreas.Count - 1; i >= 2; i--)
                {
                    ChartAreas.RemoveAt(i);
                }
            }
            //确保有且只有两个图例区
            if (Legends.Count < 1)
            {
                mLegendH = this.Legends.Add(mHLegendName);
                mLegendV = this.Legends.Add(mVLegendName);
            }
            else if (Legends.Count < 2)
            {
                mLegendH = Legends[0];
                mLegendH.Name = mHLegendName;
                mLegendV = this.Legends.Add(mVLegendName);
            }
            else
            {
                mLegendH = Legends[0];
                mLegendH.Name = mHLegendName;
                mLegendV = Legends[1];
                mLegendV.Name = mVLegendName;
                //删除多余的图例
                for (int i = Legends.Count - 1; i >= 2; i--)
                {
                    Legends.RemoveAt(i);
                }
            }


            if (_TwoPlotsMode)//使用双图时，需要调整绘图区位置
            {
                SetTwoPlotsMode();
            }
            else //单绘图模式下，自动就可以
            {
                SetOnePlotMode();
            }

            float deltaY = _PolarPlot ? 40 : 50;
            foreach (var area in this.ChartAreas)
            {
                area.AxisY.Minimum = area.AxisY.Maximum - deltaY;
            }
            Series.Clear();
            ClearEnvelops(); ;
        }
        public void ClearMarkers()
        {
            if (!_initialed) return;
            HMarkers.Clear();
            VMarkers.Clear();
            this.Annotations.Clear();
        }
        public void ClearEnvelops()
        {
            if (!_initialed) return;
            if (mEnvelopSerieV != null)
            {
                this.Series.Remove(mEnvelopSerieV);
                mEnvelopSerieV = null;
                VEnvelopPoints.Clear();
            }
            if (mEnvelopSerieH != null)
            {
                this.Series.Remove(mEnvelopSerieH);
                mEnvelopSerieH = null;
                HEnvelopPoints.Clear();
            }

        }
        public void ClearLockPlots()
        {
            if (!_initialed) return;
            if (mLockedSeries != null)
            {
                foreach (var serie in mLockedSeries)
                {
                    this.Series.Remove(serie);
                }
                mLockedSeries.Clear();
            }
            

        }
        /// <summary>
        /// 清除曲线，锁定的曲线及包络线除外
        /// 
        /// </summary>
        public void ClearPlots()
        {
            if (!_initialed) return;
            var series = this.Series.Where(s => !mLockedSeries.Contains(s) && s != mEnvelopSerieH && s != mEnvelopSerieV).ToList();
            foreach (var serie in series)
            {
                this.Series.Remove(serie);
            }
        }
       

       
        private ChartArea CreateChartArea(string name, double xmin, double xmax, double xinterval)
        {

            var area = new ChartArea(name);
            area.AxisX.Maximum = xmax;
            area.AxisX.Minimum = xmin;
            area.AxisX.Interval = xinterval;
            area.AxisX.MajorGrid.Interval = xinterval;
            area.AxisX.MajorGrid.LineColor = System.Drawing.Color.LightGray;
            area.AxisY.MajorGrid.LineColor = System.Drawing.Color.LightGray;
            area.AxisY.LineColor = System.Drawing.Color.LightGray;
            area.AxisY.MajorTickMark.LineColor = System.Drawing.Color.LightGray;
            return area;
        }
        private void SetOnePlotMode()
        {
            if (!_initialed) return;
            var list = this.ChartAreas.Where(ar => ar.Name != mHPlotName).ToList();
            if(list.Count>0)
            {
                foreach (var a in list)
                {
                    this.ChartAreas.Remove(a);
                }
            }
            
           if (this.ChartAreas.Count <= 0) this.ChartAreas.Add(mAreaH);
            mAreaH.Position.Auto = true;
            mLegendH.DockedToChartArea = DockLegend ? mHPlotName : "NotSet";
        }
        /// <summary>
        /// 双图模式下，需要手工指定双图位置，自动位置不能绘制左右分图
        /// </summary>
        private void SetTwoPlotsMode()
        {
            if (!_initialed) return;
            var list = this.ChartAreas.Where(ar => ar.Name != mHPlotName&&ar.Name!=mVPlotName).ToList();
            if (list.Count > 0)
            {
                foreach (var a in list)
                {
                    this.ChartAreas.Remove(a);
                }
            }

            if (this.ChartAreas.Count <= 0)
            {
                this.ChartAreas.Add(mAreaH);
                this.ChartAreas.Add(mAreaV);
            }
            else if (this.ChartAreas.Count <= 1)
            {
                this.ChartAreas.Add(mAreaV);
            }
            mAreaH.Position.Auto = false;
            mAreaV.Position.Auto = false;
            if (ShowLegend && DockLegend)//在曲线上叠加显示图例，五五分图
            {
                mAreaH.Position = new ElementPosition(3, 3, 47, 91);
                mAreaV.Position = new ElementPosition(53, 3, 47, 91);
                mLegendH.DockedToChartArea = mHPlotName;
                mLegendV.DockedToChartArea = mVPlotName;
            }
            else if (ShowLegend)//在独立区域上显示图例，三分图，第三个为独立的图例区
            {
                mAreaH.Position = new ElementPosition(3, 3, 33, 91);
                mAreaV.Position = new ElementPosition(36, 3, 33, 91);
                mLegendH.DockedToChartArea = "NotSet";
                mLegendV.DockedToChartArea = "NotSet";
            }
            else//不显示图例，保持为五五分图
            {
                mAreaH.Position = new ElementPosition(3, 3, 47, 91);
                mAreaV.Position = new ElementPosition(53, 3, 47, 91);
            }
           
        }

        /// <summary>
        /// 设置指定轴的最大最小和间隔
        /// </summary>
        /// <param name="axis"></param>
        /// <param name="max"></param>
        /// <param name="min"></param>
        /// <param name="interval"></param>
        public void SetAxial(Axis axis, double min, double max, double interval)
        {
            if (!_initialed) return;
            if(min>max)
            {
                var t = min;
                min = max;
                max = t;
            }
            axis.Minimum = min;
            axis.Maximum = max;
            axis.Interval = interval;
            axis.MajorGrid.Interval = interval;
        }
        #region 绘图
        /// <summary>
        /// 清除已有方向图，绘制新方向图
        /// </summary>
        /// <param name="name"></param>
        /// <param name="points"></param>
        /// <param name="areaNum"></param>
        /// <param name="clearExist">是否清除已有方向图</param>
        public void PlotPattern(string name, IEnumerable<Point32> points, int areaNum,bool clearExist=false)
        {
            if (clearExist) ClearPlots();
            AddPattern(name, points, areaNum);
        }

        /// <summary>
        /// 绘制方向图。如果已经有相同的方向图，则自动扩展序号
        /// </summary>
        /// <param name="name"></param>
        /// <param name="points"></param>
        /// <param name="areaNum">绘图区域，0-AreaH, 1- AreaV（双图模式） </param>
        public void AddPattern(string name, IEnumerable<Point32> points, int areaNum)
        {
            var serie = this.Series.LastOrDefault(s => s.Name.StartsWith(name));
            var area = mAreaH;
            var legend = mLegendH;
            if (TwoPlotsMode && areaNum > 0)
            {
                area = mAreaV;
                legend = mLegendV;
            }
            if (serie != null)
            {
                var match = Regex.Match(serie.Name, @"_\d+$");
                if (match.Success)
                {
                    int num = int.Parse(match.Value.Substring(1));
                    name = serie.Name.Substring(0, match.Index) + "_" + (num + 1);
                }
                else
                {
                    name = serie.Name + "_1";
                }
            }
            serie = this.Series.Add(name);
            serie.BorderWidth = DefaultWidth;
            serie.IsVisibleInLegend = ShowLegend;
            serie.ChartType = _PolarPlot ? SeriesChartType.Polar : SeriesChartType.Line;
            
            serie.Legend =legend.Name;
            serie.ChartArea = area.Name;
            
            foreach (var point in points)
            {
                serie.Points.AddXY(point.X, point.Y);
            }
            if (AutoDrawReferLine) PlotReferLines();
        }
        public void PlotReferLines()
        {
            AddReferLine(mAreaH);
            if (TwoPlotsMode) AddReferLine(mAreaV);
        }
        private void AddReferLine(ChartArea area)
        {
            var refPoints = new List<Point32>();
            double step = 1;
            if (area == mAreaH)
            {
                for (double p = PhiMin; p <= PhiMax; p+=step)
                {
                    refPoints.Add(new Point32((float)p, (float)(MaxLevelHor - Math.Abs(ReferenceLineLevel))));
                }
            }
            else
            {
                for (double p = ThetaMin; p <= ThetaMax; p+=step)
                {
                    refPoints.Add(new Point32((float)p, (float)(MaxLevelVer - Math.Abs(ReferenceLineLevel))));
                }
            }
            string name = area == mAreaH ? "HRef" : "VRef";
            var serie = this.Series.FirstOrDefault(s => s.Name == name);
            if (serie == null)
            {
                serie = this.Series.Add(name);
                serie.ChartType = _PolarPlot ? SeriesChartType.Polar : SeriesChartType.Line;
                serie.ChartArea = area == mAreaH ? mHPlotName : mVPlotName;
                serie.IsVisibleInLegend = false;
                serie.BorderWidth = 1;
                serie.BorderDashStyle = ChartDashStyle.Dash;
                serie.Color = System.Drawing.Color.Magenta;
            }
            serie.Points.Clear();
            foreach (var point in refPoints)
            {
                serie.Points.AddXY(point.X, point.Y);
            }
        }
        /// <summary>
        /// 锁定名称为name的曲线
        /// </summary>
        /// <param name="name"></param>
        public void LockPlot(string name)
        {
            var serie = Series.FirstOrDefault(s => s.Name == name);
            if(serie!=null)
            {
                mLockedSeries.Add(serie);
            }
        }
        /// <summary>
        /// 反锁名称为name的曲线
        /// </summary>
        /// <param name="name"></param>
        public void UnLockPlot(string name)
        {
            var serie = mLockedSeries.FirstOrDefault(s => s.Name == name);
            if (serie != null)
            {
                mLockedSeries.Remove(serie);
            }
        }
       
        #endregion
        
        private bool IsReferLine(Series serie)
        {
            return serie.Name == "HRef" || serie.Name == "VRef";
        }
        private bool IsEnvelopLine(Series serie)
        {
            return serie == mEnvelopSerieH || serie == mEnvelopSerieV;
        }
        /// <summary>
        /// 鼠标移动时，获取当前点，输出到CurrDataPoint
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void chart_MouseMove(object sender, MouseEventArgs e)
        {
            if (!_initialed) return;
            CurrHit = this.HitTest(e.X, e.Y);
            if (EnvelopMode)
            {
                CurrDataPoint = CurrHit.ChartArea.GetDataPointFromMousePosition(e.X, e.Y);
            }
            else
            {
                if (CurrHit.Series != null && !IsReferLine(CurrHit.Series) && !IsEnvelopLine(CurrHit.Series))
                {
                    Cursor = Cursors.Cross;
                    this.HighlightSerie( CurrHit.Series, false,_HighlightWidth,_DefaultWidth);
                    if (CurrHit.PointIndex >= 0)
                    {
                        CurrDataPoint = CurrHit.Series.Points[CurrHit.PointIndex];
                    }
                }
                else
                {
                    Cursor = Cursors.Default;
                }
            }
        }

        private void chart_MouseClick(object sender, MouseEventArgs e)
        {
            CurrHit = this.HitTest(e.X, e.Y);
            if (CurrHit.ChartArea == null) return;
            //选中不同的区域时，使用不同的数据
            var dpoints = CurrHit.ChartArea == mAreaH ? HEnvelopPoints : VEnvelopPoints;
            var evnName = "Envelop" + (CurrHit.ChartArea == mAreaH ? "H" : "V");
            if (EnvelopMode)//包络线模式，取点输入包络线
            {
                var dp = CurrHit.ChartArea.GetDataPointFromMousePosition( e.X, e.Y);

                if (dpoints.Count <= 0)//第一个点，创建新包络线
                {
                    dpoints.Add(dp);
                    var envSerie = this.Series.Add(evnName);
                    envSerie.ChartType = SeriesChartType.FastLine;
                    envSerie.IsVisibleInLegend = false;
                    envSerie.Color = System.Drawing.Color.DarkGreen;
                    envSerie.BorderDashStyle = ChartDashStyle.Dash;
                    envSerie.BorderWidth = 2;
                    envSerie.ChartArea = CurrHit.ChartArea.Name;
                    //暂存包络线
                    if (CurrHit.ChartArea == mAreaH)
                    {
                        mEnvelopSerieH = envSerie;
                    }
                    else
                    {
                        mEnvelopSerieV = envSerie;
                    }
                }
                else
                {
                    dpoints.Add(dp);//新增数据点到包络线
                }
                dpoints = dpoints.OrderBy(p => p.XValue).ToList();
                //重绘当前包络线
                var serie = CurrHit.ChartArea == mAreaH ? mEnvelopSerieH : mEnvelopSerieV;
                serie.Points.Clear();
                foreach (var point in dpoints)
                {
                    serie.Points.AddXY(point.XValue, point.YValues[0]);
                }
            }
            else //曲线频标模式，绘制数据标志
            {
                if (CurrHit.Series != null && !IsReferLine(CurrHit.Series) && !IsEnvelopLine(CurrHit.Series))
                {
                    Cursor = Cursors.Cross;
                    var makers = CurrHit.ChartArea == mAreaH ? HMarkers : VMarkers;
                    makers.Add(this.AddMarker( CurrHit, true, makers.Count));
                }
                else
                {
                    Cursor = Cursors.Default;
                }
            }
        }

        private void chart_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            CurrHit = this.HitTest(e.X, e.Y);
        }
        /// <summary>
        /// 清除所有数据
        /// </summary>
        public void ClearAll()
        {
            ClearEnvelops();
            ClearLockPlots();
            ClearMarkers();
            ClearPlots();
        }
    }
}
